use serde::{Deserialize, Serialize};
use crate::devices::protocol::binary;

pub const SC_CONTROL_MSG_INJECT_TEXT_MAX_LENGTH: usize = 300;
pub const SC_CONTROL_MSG_MAX_SIZE: usize = 1 << 18; // 256k
pub const SC_CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH: usize = SC_CONTROL_MSG_MAX_SIZE - 14;

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[repr(u8)]
pub enum ControlMsgType {
    InjectKeycode = 0,
    InjectText = 1,
    InjectTouchEvent = 2,
    InjectScrollEvent = 3,
    BackOrScreenOn = 4,
    ExpandNotificationPanel = 5,
    ExpandSettingsPanel = 6,
    CollapsePanels = 7,
    GetClipboard = 8,
    SetClipboard = 9,
    SetScreenPowerMode = 10,
    RotateDevice = 11,
    UhidCreate = 12,
    UhidInput = 13,
    OpenHardKeyboardSettings = 14,
}

impl ControlMsgType {
    pub fn from_u8(value: u8) -> Option<Self> {
        match value {
            0 => Some(Self::InjectKeycode),
            1 => Some(Self::InjectText),
            2 => Some(Self::InjectTouchEvent),
            3 => Some(Self::InjectScrollEvent),
            4 => Some(Self::BackOrScreenOn),
            5 => Some(Self::ExpandNotificationPanel),
            6 => Some(Self::ExpandSettingsPanel),
            7 => Some(Self::CollapsePanels),
            8 => Some(Self::GetClipboard),
            9 => Some(Self::SetClipboard),
            10 => Some(Self::SetScreenPowerMode),
            11 => Some(Self::RotateDevice),
            12 => Some(Self::UhidCreate),
            13 => Some(Self::UhidInput),
            14 => Some(Self::OpenHardKeyboardSettings),
            _ => None,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Position {
    pub x: i32,
    pub y: i32,
    pub w: u16,  // screen width
    pub h: u16,  // screen height
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum ControlMessage {
    InjectKeycode {
        action: u8,
        keycode: u32,
        repeat: u32,
        metastate: u32,
    },
    InjectText {
        text: String,
    },
    InjectTouchEvent {
        action: u8,
        pointer_id: u64,
        position: Position,
        pressure: f32,
        action_button: u32,
        buttons: u32,
    },
    InjectScrollEvent {
        position: Position,
        hscroll: f32,
        vscroll: f32,
        buttons: u32,
    },
    BackOrScreenOn {
        action: u8,
    },
    GetClipboard {
        copy_key: u8,
    },
    SetClipboard {
        sequence: u64,
        text: String,
        paste: bool,
    },
    SetScreenPowerMode {
        mode: u8,
    },
    UhidCreate {
        id: u16,
        report_desc_size: u16,
        report_desc: Vec<u8>,
    },
    UhidInput {
        id: u16,
        size: u16,
        data: Vec<u8>,
    },
    ExpandNotificationPanel,
    ExpandSettingsPanel,
    CollapsePanels,
    RotateDevice,
    OpenHardKeyboardSettings,
}

pub fn generate_control_message(msg_type: ControlMsgType, payload: &serde_json::Value) -> Vec<u8> {
    match msg_type {
        ControlMsgType::InjectKeycode => generate_inject_keycode_msg(
            msg_type as u8,
            payload["action"].as_u64().unwrap() as u8,
            payload["keycode"].as_u64().unwrap() as u32,
            payload["repeat"].as_u64().unwrap() as u32,
            payload["metastate"].as_u64().unwrap() as u32,
        ),
        ControlMsgType::InjectText => {
            let mut buf: Vec<u8> = vec![msg_type as u8];
            let text = payload["text"].as_str().unwrap();
            binary::write_string(text, SC_CONTROL_MSG_INJECT_TEXT_MAX_LENGTH, &mut buf);
            buf
        }
        ControlMsgType::InjectTouchEvent => generate_inject_touch_msg(
            msg_type as u8,
            payload["action"].as_u64().unwrap() as u8,
            payload["pointerId"].as_u64().unwrap(),
            payload["position"]["x"].as_i64().unwrap() as i32,
            payload["position"]["y"].as_i64().unwrap() as i32,
            payload["position"]["w"].as_i64().unwrap() as u16,
            payload["position"]["h"].as_i64().unwrap() as u16,
            binary::float_to_u16fp(payload["pressure"].as_f64().unwrap() as f32),
            payload["actionButton"].as_u64().unwrap() as u32,
            payload["buttons"].as_u64().unwrap() as u32,
        ),
        ControlMsgType::InjectScrollEvent => {
            let mut buf = vec![0; 21];
            buf[0] = msg_type as u8;
            binary::write_position(
                &mut buf[1..13],
                payload["position"]["x"].as_i64().unwrap() as i32,
                payload["position"]["y"].as_i64().unwrap() as i32,
                payload["position"]["w"].as_i64().unwrap() as u16,
                payload["position"]["h"].as_i64().unwrap() as u16,
            );
            binary::write_16be(
                &mut buf[13..15],
                binary::float_to_i16fp(payload["hscroll"].as_f64().unwrap() as f32) as u16,
            );
            binary::write_16be(
                &mut buf[15..17],
                binary::float_to_i16fp(payload["vscroll"].as_f64().unwrap() as f32) as u16,
            );
            binary::write_32be(
                &mut buf[17..21],
                payload["buttons"].as_u64().unwrap() as u32,
            );
            buf
        }
        ControlMsgType::BackOrScreenOn => {
            vec![
                msg_type as u8,
                payload["action"].as_u64().unwrap() as u8,
            ]
        }
        ControlMsgType::GetClipboard => {
            vec![
                msg_type as u8,
                payload["copyKey"].as_u64().unwrap() as u8,
            ]
        }
        ControlMsgType::SetClipboard => {
            let mut buf: Vec<u8> = vec![0; 10];
            buf[0] = msg_type as u8;
            binary::write_64be(&mut buf[1..9], payload["sequence"].as_u64().unwrap());
            buf[9] = payload["paste"].as_bool().unwrap_or(false) as u8;
            let text = payload["text"].as_str().unwrap();
            binary::write_string(text, SC_CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH, &mut buf);
            buf
        }
        ControlMsgType::SetScreenPowerMode => {
            vec![msg_type as u8, payload["mode"].as_u64().unwrap() as u8]
        }
        ControlMsgType::UhidCreate => {
            let size = payload["reportDescSize"].as_u64().unwrap() as u16;
            let mut buf: Vec<u8> = vec![0; 5];
            buf[0] = msg_type as u8;
            binary::write_16be(&mut buf[1..3], payload["id"].as_u64().unwrap() as u16);
            binary::write_16be(&mut buf[3..5], size);
            let report_desc = payload["reportDesc"].as_array().unwrap();
            let report_desc_u8: Vec<u8> = report_desc
                .iter()
                .map(|x| x.as_u64().unwrap() as u8)
                .collect();
            buf.extend_from_slice(&report_desc_u8);
            buf
        }
        ControlMsgType::UhidInput => {
            let size = payload["size"].as_u64().unwrap() as u16;
            let mut buf: Vec<u8> = vec![0; 5];
            buf[0] = msg_type as u8;
            binary::write_16be(&mut buf[1..3], payload["id"].as_u64().unwrap() as u16);
            binary::write_16be(&mut buf[3..5], size);
            let data = payload["data"].as_array().unwrap();
            let data_u8: Vec<u8> = data.iter().map(|x| x.as_u64().unwrap() as u8).collect();
            buf.extend_from_slice(&data_u8);
            buf
        }
        _ => {
            vec![msg_type as u8]
        }
    }
}

fn generate_inject_keycode_msg(
    msg_type: u8,
    action: u8,
    keycode: u32,
    repeat: u32,
    metastate: u32,
) -> Vec<u8> {
    let mut buf = vec![0; 14];
    buf[0] = msg_type;
    buf[1] = action;
    binary::write_32be(&mut buf[2..6], keycode);
    binary::write_32be(&mut buf[6..10], repeat);
    binary::write_32be(&mut buf[10..14], metastate);
    buf
}

fn generate_inject_touch_msg(
    msg_type: u8,
    action: u8,
    pointer_id: u64,
    x: i32,
    y: i32,
    w: u16,
    h: u16,
    pressure: u16,
    action_button: u32,
    buttons: u32,
) -> Vec<u8> {
    let mut buf = vec![0; 32];
    buf[0] = msg_type;
    buf[1] = action;
    binary::write_64be(&mut buf[2..10], pointer_id);
    binary::write_position(&mut buf[10..22], x, y, w, h);
    binary::write_16be(&mut buf[22..24], pressure);
    binary::write_32be(&mut buf[24..28], action_button);
    binary::write_32be(&mut buf[28..32], buttons);
    buf
}

// Screen power modes
pub const SC_SCREEN_POWER_MODE_OFF: u8 = 0;
pub const SC_SCREEN_POWER_MODE_NORMAL: u8 = 2;

// Copy key modes
pub const SC_COPY_KEY_NONE: u8 = 0;
pub const SC_COPY_KEY_COPY: u8 = 1;
pub const SC_COPY_KEY_CUT: u8 = 2;